package heap;

import java.util.Arrays;

public class MinHeap {

    int[] array;

    int size;

    public MinHeap(int capacity) {
        this.array = new int[capacity];
    }

    public MinHeap(int[] array) {
        this.array = array;
        this.size = array.length;
        heapify();
    }

    /**
     * 建堆-背下来，要求必会
     * 1.找到最后一个非叶子节点 size / 2 - 1
     * 2.从后向前，对每个节点执行下潜
     */
    private void heapify() {
        for (int i = size >> 2 - 1; i >=0; i--) {
            down(i);
        }
    }

    /**
     * 删除堆顶元素
     * @return 堆顶元素
     */
    public int poll() {
        // 堆顶与堆尾元素进行交换，然后删除堆尾元素，之后进行下潜操作
        int top = array[0];
        swap(0, size - 1);
        size--;
        down(0);
        return top;
    }

    /**
     * 删除指定索引位置元素
     * @param index 索引
     * @return 被删除元素
     */
    public int poll(int index) {
        int deleted = array[index];
        swap(index, size - 1);
        size--;
        down(index);
        return deleted;
    }

    /**
     * 获取堆顶元素
     * @return 堆顶元素
     */
    public int peek() {
        return array[0];
    }

    /**
     * 替换堆顶元素
     * @param replaced 新元素
     */
    public void replace(int replaced) {
        array[0] = replaced;
        down(0);
    }

    /**
     * 堆的尾部添加元素
     * @param offered 被添加元素值
     * @return 是否添加成功
     */
    public boolean offer(int offered) {
        if (size == array.length) {
            return false;
        }
        up(offered);
        size++;
        return true;
    }

    // 将inserted元素上浮，直至offered小于父元素或到堆顶
    private void up(int offered) {
        int child = size; // 子节点指针
        while (child > 0) {
            int parent = (child - 1) >> 2; // 父节点指针
            if (offered < array[parent]) {
                array[child] = array[parent];
            } else {
                break;
            }
            child = parent;
        }
        array[child] = offered;
    }

    // 将parent索引处的元素下潜，与两个孩子较大者交换，直至没孩子或没孩子比他大
    private void down(int parent) {
        while (true) {
            int left = parent * 2 + 1;
            int right = parent * 2 + 2;
            int min = parent;

            if (left < size && array[left] < array[min]) {
                min = left;
            }
            if (right < size && array[right] < array[min]) {
                min = right;
            }

            if (min != parent) {
                swap(min, parent);
                parent = min; // 更新parent为max，继续向下调整
            } else {
                break; // 如果没有更大的子节点，退出循环
            }
        }
    }

    // 交换两个索引处的元素
    private void swap(int i, int j) {
        int t = array[i];
        array[i] = array[j];
        array[j] = t;
    }

    public static void main(String[] args) {
        int[] array = {1, 2, 3, 4, 5, 6, 7, 8};
        MinHeap maxHeap = new MinHeap(array);
        System.out.println(Arrays.toString(maxHeap.array));

        // 堆排序
        // 将堆顶元素（最大值）与堆的最后一个元素交换，这样最大值就放在了数组的末尾，缩小堆的范围。
        // 对剩余的元素继续进行下滤操作，恢复堆的性质。
        // 重复此过程，直到整个数组排序完成。
        while (maxHeap.size > 1) {
            maxHeap.swap(0, maxHeap.size - 1);
            maxHeap.size--;
            maxHeap.down(0);
        }
        System.out.println(Arrays.toString(maxHeap.array));
    }

}
